using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;

namespace OpenTK.Windowing.Desktop
{
	public static class Monitors
	{
		private static readonly GLFWCallbacks.MonitorCallback _monitorCallback;

		[Obsolete("Use GetMonitors() instead.", true)]
		public static int Count
		{
			get
			{
				throw new Exception("This property is Obsolete, use GetMonitors() instead.");
			}
		}

		public static event Action<MonitorEventArgs> OnMonitorConnected;

		unsafe static Monitors()
		{
			GLFWProvider.EnsureInitialized();
			_monitorCallback = MonitorCallback;
			GLFW.SetMonitorCallback(_monitorCallback);
		}

		public static float GetPlatformDefaultDpi()
		{
			if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
			{
				return 72f;
			}
			return 96f;
		}

		private static int GetRectangleIntersectionArea(Box2i a, Box2i b)
		{
			Box2i box2i = Box2i.Intersect(a, b);
			return box2i.Size.X * box2i.Size.Y;
		}

		public unsafe static MonitorInfo GetPrimaryMonitor()
		{
			return new MonitorInfo(new MonitorHandle((IntPtr)GLFW.GetPrimaryMonitor()));
		}

		public unsafe static List<MonitorInfo> GetMonitors()
		{
			int count;
			Monitor** monitorsRaw = GLFW.GetMonitorsRaw(out count);
			List<MonitorInfo> list = new List<MonitorInfo>(count);
			for (int i = 0; i < count; i++)
			{
				list.Add(new MonitorInfo(new MonitorHandle((IntPtr)monitorsRaw[i])));
			}
			return list;
		}

		public unsafe static MonitorInfo GetMonitorFromWindow(Window* window)
		{
			MonitorHandle handle = new MonitorHandle((IntPtr)GLFW.GetWindowMonitor(window));
			if (handle.Pointer != IntPtr.Zero)
			{
				return new MonitorInfo(handle);
			}
			GLFW.GetWindowPos(window, out var x, out var y);
			GLFW.GetWindowSize(window, out var width, out var height);
			Box2i b = new Box2i(x, y, x + width, y + height);
			List<MonitorInfo> monitors = GetMonitors();
			int index = 0;
			for (int i = 0; i < monitors.Count; i++)
			{
				if (GetRectangleIntersectionArea(monitors[i].ClientArea, b) > GetRectangleIntersectionArea(monitors[index].ClientArea, b))
				{
					index = i;
				}
			}
			return monitors[index];
		}

		public unsafe static MonitorInfo GetMonitorFromWindow(NativeWindow window)
		{
			return GetMonitorFromWindow(window.WindowPtr);
		}

		private unsafe static void MonitorCallback(Monitor* monitor, ConnectedState state)
		{
			Monitors.OnMonitorConnected?.Invoke(new MonitorEventArgs(new MonitorHandle((IntPtr)monitor), state == ConnectedState.Connected));
		}

		[Obsolete("Use GetMonitors() instead.", true)]
		public static bool TryGetMonitorInfo(int index, out MonitorInfo info)
		{
			throw new Exception("This function is Obsolete, use GetMonitors() instead.");
		}

		[Obsolete("Use GetMonitors() instead.", true)]
		public static bool TryGetMonitorInfo(MonitorHandle monitor, out MonitorInfo info)
		{
			throw new Exception("This function is Obsolete, use GetMonitors() instead.");
		}

		[Obsolete("There is no cache anymore", true)]
		public static bool CheckCache()
		{
			throw new Exception("There is no cache anymore, don't call this function.");
		}

		[Obsolete("There is no cache anymore", true)]
		public static void BuildMonitorCache()
		{
			throw new Exception("There is no cache anymore, don't call this function.");
		}
	}
}
